#include <stdio.h>
#include "string.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_log.h"
#include "esp_check.h"
#include "driver/uart.h"
#include "nvs_storage_hal.h"
#include "driver/gpio.h"
#include "hlw8032.h"

static const char *TAG = "hlw8032";

static SemaphoreHandle_t xHlwSemaphore;
static hlw8032_uart_handle_data private_hlw8032;
static hlw8032_data hlw8032_real_data;
static QueueHandle_t uart1_queue;

/**
 * @brief hlw8032_data_bzero
 *
 * @param private_hlw8032
 * @param hlw8032_calculation_data
 * @param bzero_data_type
 */
static void hlw8032_data_bzero(hlw8032_uart_handle_data *private_hlw8032, hlw8032_data *hlw8032_calculation_data, hlw8032_bzero_data_type_t bzero_data_type)
{
    switch (bzero_data_type)
    {
    case BZERO_EFFECTIVE_VOLTAGE:
        hlw8032_calculation_data->effective_voltage = 0;
        break;
    case BZERO_EFFECTIVE_CURRENT:
        hlw8032_calculation_data->effective_current = 0;
        break;
    case BZERO_POWER:
        hlw8032_calculation_data->active_power = 0;
        hlw8032_calculation_data->apparent_power = 0;
        break;
    case BZERO_PARAMETER:
        /* parameter bzero */
        private_hlw8032->state_reg = 0;
        private_hlw8032->check_reg = 0;
        private_hlw8032->voltage_par_reg = 0;
        private_hlw8032->voltage_reg = 0;
        private_hlw8032->current_par_reg = 0;
        private_hlw8032->current_reg = 0;
        private_hlw8032->power_par_reg = 0;
        private_hlw8032->power_reg = 0;
        break;
    case BZERO_ALL_BZERO:
        /* parameter bzero */
        private_hlw8032->state_reg = 0;
        private_hlw8032->check_reg = 0;
        private_hlw8032->voltage_par_reg = 0;
        private_hlw8032->voltage_reg = 0;
        private_hlw8032->current_par_reg = 0;
        private_hlw8032->current_reg = 0;
        private_hlw8032->power_par_reg = 0;
        private_hlw8032->power_reg = 0;
        /* data bzero */
        hlw8032_calculation_data->effective_voltage = 0;
        hlw8032_calculation_data->effective_current = 0;
        hlw8032_calculation_data->active_power = 0;
        hlw8032_calculation_data->apparent_power = 0;
        hlw8032_calculation_data->factor = 0;
        break;
    default:
        break;
    }
}

/**
 * @brief hlw8032_check
 *
 * @param private_hlw8032
 * @param hlw8032_calculation_data
 * @param buffer data
 * @param length date's length
 * @return esp_err_t
 */
static esp_err_t hlw8032_check(hlw8032_uart_handle_data *private_hlw8032, hlw8032_data *hlw8032_calculation_data, uint8_t *buffer, uint32_t length)
{
    esp_err_t ret = ESP_FAIL;
    uint8_t check = 0; /* low 8 bit */
    /* check hlw8032 data length start */
    if (length != 24)
    {
        hlw8032_data_bzero(private_hlw8032, hlw8032_calculation_data, BZERO_ALL_BZERO); /* hlw8032 data bzero */
#if HLW8032_DEBUG
        ESP_GOTO_ON_ERROR(ret, err, TAG, "Not enough data received from the HLW8032 [24]");
#endif
        return ESP_FAIL;
    }
    /* check hlw8032 data length end */

    /* check hlw8032 state start */
    if (buffer[0] == 0xAA)
    {
        hlw8032_data_bzero(private_hlw8032, hlw8032_calculation_data, BZERO_ALL_BZERO); /* hlw8032 data bzero */
#if HLW8032_DEBUG
        ESP_GOTO_ON_ERROR(ret, err, TAG, "HLW8032 status register error [State REG:%#X]", buffer[0]);
#endif
        return ESP_FAIL;
    }
    /* check hlw8032 state end */

    /* check hlw8032 check reg start */
    if (buffer[1] != 0x5A)
    {
        hlw8032_data_bzero(private_hlw8032, hlw8032_calculation_data, BZERO_ALL_BZERO); /* hlw8032 data bzero */
#if HLW8032_DEBUG
        ESP_GOTO_ON_ERROR(ret, err, TAG, "HLW8032 status register error [Check REG:%#X]", buffer[1]);
#endif
        return ESP_FAIL;
    }
    /* check hlw8032 check reg end */

    /* Calculate the checksum start */
    for (uint8_t i = 2; i <= 22; i++)
    {
        check = check + buffer[i];
    }
    if (check != buffer[23])
    {
        hlw8032_data_bzero(private_hlw8032, hlw8032_calculation_data, BZERO_ALL_BZERO); /* hlw8032 data bzero */
#if HLW8032_DEBUG
        ESP_GOTO_ON_ERROR(ret, err, TAG, "HLW8032 status register error [Checksum REG]");
#endif
        return ESP_FAIL;
    }
    else
    {
#if HLW8032_DEBUG
        for (int i = 0; i < length; i++)
        {
            printf("%#X ", buffer[i]);
        }
        printf("\r\n");
#endif
        return ESP_OK;
    }

#if HLW8032_DEBUG
err:
    if (ret != ESP_OK)
    {

        ESP_LOGE(TAG, "Error (ret:%d)! HLW8032 check failed![function:%s]", ret, __func__);

        return ESP_FAIL;
    }
#endif
    return ESP_OK;
    /* Calculate the checksum end */
}

/**
 * @brief hlw8032_handle_uart_data
 *
 * @param private_hlw8032
 * @param hlw8032_calculation_data
 * @param buffer
 * @return esp_err_t
 */
static esp_err_t hlw8032_handle_uart_data(hlw8032_uart_handle_data *private_hlw8032, hlw8032_data *hlw8032_calculation_data, uint8_t *buffer)
{

    private_hlw8032->voltage_par_reg = (((uint32_t)buffer[2] << 16) |
                                        ((uint32_t)buffer[3] << 8) |
                                        (buffer[4]));
    /* ensure voltage_reg update */
    if ((buffer[20] & 0x40) != 0)
    {
        private_hlw8032->voltage_reg = (((uint32_t)buffer[5] << 16) |
                                        ((uint32_t)buffer[6] << 8) |
                                        (buffer[7]));
    }
    private_hlw8032->current_par_reg = (((uint32_t)buffer[8] << 16) |
                                        ((uint32_t)buffer[9] << 8) |
                                        (buffer[10]));
    /* ensure current_reg update */
    if ((buffer[20] & 0x20) != 0)
    {
        private_hlw8032->current_reg = (((uint32_t)buffer[11] << 16) |
                                        ((uint32_t)buffer[12] << 8) |
                                        (buffer[13]));
    }
    private_hlw8032->power_par_reg = (((uint32_t)buffer[14] << 16) |
                                      ((uint32_t)buffer[15] << 8) |
                                      (buffer[16]));
    /* ensure power_reg update */
    if ((buffer[20] & 0x16) != 0)
    {
        private_hlw8032->power_reg = (((uint32_t)buffer[17] << 16) |
                                      ((uint32_t)buffer[18] << 8) |
                                      (buffer[19]));
    }
    /* ensure pf_reg update */
    private_hlw8032->PF_reg_sate_now = (buffer[20] & 0x80); /* now  */
    if (private_hlw8032->PF_reg_sate_now != private_hlw8032->PF_reg_sate_last)
    {
        private_hlw8032->pf_reg_sum++;
        nvs_flash_set(HLW8032_NVS_NAME, HLW8032_NVS_KEY, private_hlw8032->pf_reg_sum);
        private_hlw8032->PF_reg_sate_last = private_hlw8032->PF_reg_sate_now;
    }
    private_hlw8032->pf_reg = (((uint16_t)buffer[21] << 8) |
                               (buffer[22]));

    /* calculating real data start */
    if (buffer[0] == 0xF8) /* Voltage register overflow */
    {
        hlw8032_calculation_data->effective_voltage = 0; /* hlw8032 handle voltage data bzero */
    }
    else
    {
        hlw8032_calculation_data->effective_voltage = (((float)private_hlw8032->voltage_par_reg) / ((float)private_hlw8032->voltage_reg)) * VOLTAGE_COEFFICIENT;
    }
    if (buffer[0] == 0xF4) /* Current register overflow */
    {
        hlw8032_calculation_data->effective_current = 0; /* hlw8032 current data bzero */
    }
    else
    {
        hlw8032_calculation_data->effective_current = (((float)private_hlw8032->current_par_reg) / ((float)private_hlw8032->current_reg)) * CURRENT_COEFFICIENT;
    }
    if (buffer[0] == 0xF2) /* Power register overflow */
    {
        hlw8032_calculation_data->active_power = 0; /* hlw8032 power data bzero */
        hlw8032_calculation_data->apparent_power = 0;
        hlw8032_calculation_data->factor = 0;
    }
    else
    {
        hlw8032_calculation_data->active_power = (((float)private_hlw8032->power_par_reg) / ((float)private_hlw8032->power_reg)) * VOLTAGE_COEFFICIENT * CURRENT_COEFFICIENT;
        hlw8032_calculation_data->apparent_power = hlw8032_calculation_data->effective_voltage * hlw8032_calculation_data->effective_current;
        hlw8032_calculation_data->factor = hlw8032_calculation_data->active_power / hlw8032_calculation_data->apparent_power;
    }
    hlw8032_calculation_data->pf_count = (private_hlw8032->pf_reg_sum) * 65536 + private_hlw8032->pf_reg;
    hlw8032_calculation_data->one_pulses = 3600000000000 / (private_hlw8032->power_par_reg) / VOLTAGE_COEFFICIENT / CURRENT_COEFFICIENT;
    hlw8032_calculation_data->electricity_calculation = (float)hlw8032_calculation_data->pf_count / (float)hlw8032_calculation_data->one_pulses;
    /* calculating real data end */

#if HLW8032_DEBUG
    ESP_LOGI(TAG,
             "有效电压:%.3fV,有效电流:%.3fA,有功功率:%.3fW,视在功率:%.3fW,功率因数:%.3f,脉冲数量统计:%ld, 1度电对应脉冲数量:%ld,当前电量(KW.h):%.6f",
             hlw8032_calculation_data->effective_voltage,
             hlw8032_calculation_data->effective_current,
             hlw8032_calculation_data->active_power,
             hlw8032_calculation_data->apparent_power,
             hlw8032_calculation_data->factor,
             hlw8032_calculation_data->pf_count,
             hlw8032_calculation_data->one_pulses,
             hlw8032_calculation_data->electricity_calculation);
#endif
    return ESP_OK;
}

/**
 * @brief hlw8032_parameter_handle_uart_data
 *
 * @param private_hlw8032
 * @param hlw8032_calculation_data
 * @param buffer
 * @param length
 * @return esp_err_t
 */
static esp_err_t hlw8032_parameter_handle_uart_data(hlw8032_uart_handle_data *private_hlw8032, hlw8032_data *hlw8032_calculation_data, uint8_t *buffer, uint32_t length)
{
    switch (hlw8032_check(private_hlw8032, hlw8032_calculation_data, buffer, length))
    {
    case ESP_OK:
        return hlw8032_handle_uart_data(private_hlw8032, hlw8032_calculation_data, buffer);
    default:
#if HLW8032_DEBUG
        ESP_LOGW(TAG, "HLW8032 chip error or no electrical appliances");
#endif
        return ESP_FAIL;
    }
}

/**
 * @brief hlw8032_get_data
 *
 * @param data_type
 * @return float
 */
float hlw8032_get_data(hlw8032_get_data_type_t data_type)
{
    switch (data_type)
    {
    case EFFECTIVE_VOLTAGE:
        return hlw8032_real_data.effective_voltage;
    case EFFECTIVE_CURRENT:
        return hlw8032_real_data.effective_current;
    case ACTIVE_POWER:
        return hlw8032_real_data.active_power;
    case APPARENT_POWER:
        return hlw8032_real_data.apparent_power;
    case FACTOR:
        return hlw8032_real_data.factor;
    case PF_COUNT:
        return hlw8032_real_data.pf_count;
    case ONE_PULSES:
        return hlw8032_real_data.one_pulses;
    case EFFECTIVE_CALCULATION:
        return hlw8032_real_data.electricity_calculation;
    default:
        return (0.0);
    }
}

static void hlw8032_init(void)
{
    esp_log_level_set(TAG, ESP_LOG_INFO);

    /* hlw mutex create start */
    xHlwSemaphore = xSemaphoreCreateMutex();
    if (xHlwSemaphore != NULL)
    {
        ESP_LOGI(TAG, "Creat xHlwSemaphore successfully");
    }
    else
    {
        /* error handler begin */
        ESP_LOGE(TAG, "Creat xHlwSemaphore error. err msg->%s,,%s", __FILE__, __func__);
        vSemaphoreDelete(xHlwSemaphore);
        /* error handler end */
    }
    /* hlw mutex create end */

    /* Configure parameters of an UART driver,
     * communication pins and install the driver */
    uart_config_t uart_config = {
        .baud_rate = 4800,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_EVEN,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    // Install UART driver, and get the queue.
    uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart1_queue, 0);
    uart_param_config(EX_UART_NUM, &uart_config);

    // Set UART log level
    esp_log_level_set(TAG, ESP_LOG_INFO);
    // Set UART pins (using UART0 default pins ie no changes.)
    uart_set_pin(EX_UART_NUM, HLW8032_TXD_PIN, HLW8032_RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

    // Set uart pattern detect function.
    uart_enable_pattern_det_baud_intr(EX_UART_NUM, '+', PATTERN_CHR_NUM, 9, 0, 0);
    // Reset the pattern queue length to record at most 20 pattern positions.
    uart_pattern_queue_reset(EX_UART_NUM, 20);

    /* init hlw8032 nvs start */
    private_hlw8032.PF_reg_sate_last = 0;
    private_hlw8032.PF_reg_sate_now = 0;
    nvs_flash_get(HLW8032_NVS_NAME, HLW8032_NVS_KEY);
    // nvs_flash_set(HLW8032_NVS_NAME, HLW8032_NVS_KEY, nvs_flash_get(HLW8032_NVS_NAME, HLW8032_NVS_KEY));
    private_hlw8032.pf_reg_sum = nvs_flash_get(HLW8032_NVS_NAME, HLW8032_NVS_KEY);
    /* init hlw8032 nvs end */
}

void hlw8032_task(void *arg)
{
    uart_event_t event;
    size_t buffered_size;
    uint8_t *dtmp = (uint8_t *)malloc(RD_BUF_SIZE);

    hlw8032_init();

    while (1)
    {
        // Waiting for UART event.
        if (xQueueReceive(uart1_queue, (void *)&event, (TickType_t)portMAX_DELAY))
        {
            bzero(dtmp, RD_BUF_SIZE); /* clear dtmp[Σ] = 0 */
#if HLW8032_DEBUG
            ESP_LOGI(TAG, "uart[%d] event:", EX_UART_NUM);
#endif
            switch (event.type)
            {
            case UART_DATA:
#if HLW8032_DEBUG
                ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
#endif
                /* Event of UART receving data */
                /* We'd better handler data event fast, there would be much more data events than
                other types of events. If we take too much time on data event, the queue might
                be full. */
                uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY); /* read uart bytes */
                /* handle hlw data start */
                if (pdTRUE == xSemaphoreTake(xHlwSemaphore, portMAX_DELAY))
                {
                    hlw8032_parameter_handle_uart_data(&private_hlw8032, &hlw8032_real_data, dtmp, event.size); /* handle hlw8032 uart data */
                    xSemaphoreGive(xHlwSemaphore);                                                              /* release hlw mutex */
                }
                /* handle hlw data end */
                break;
            case UART_FIFO_OVF: /* Event of HW FIFO overflow detected */
#if HLW8032_DEBUG
                ESP_LOGI(TAG, "hw fifo overflow");
#endif
                // If fifo overflow happened, you should consider adding flow control for your application.
                // The ISR has already reset the rx FIFO,
                // As an example, we directly flush the rx buffer here in order to read more data.
                uart_flush_input(EX_UART_NUM);
                xQueueReset(uart1_queue);
                break;
            case UART_BUFFER_FULL: /* Event of UART ring buffer full */
#if HLW8032_DEBUG
                ESP_LOGI(TAG, "ring buffer full");
#endif
                // If buffer full happened, you should consider increasing your buffer size
                // As an example, we directly flush the rx buffer here in order to read more data.
                uart_flush_input(EX_UART_NUM);
                xQueueReset(uart1_queue);
                break;
            case UART_BREAK: /* Event of UART RX break detected */
#if HLW8032_DEBUG
                ESP_LOGI(TAG, "uart rx break");
#endif
                break;
            case UART_PARITY_ERR: /* Event of UART parity check error */
#if HLW8032_DEBUG
                ESP_LOGI(TAG, "uart parity error");
#endif
                break;
            case UART_FRAME_ERR: /* Event of UART frame error */
#if HLW8032_DEBUG
                ESP_LOGI(TAG, "uart frame error");
#endif
                break;
            case UART_PATTERN_DET: /* UART_PATTERN_DET */
                uart_get_buffered_data_len(EX_UART_NUM, &buffered_size);
                int pos = uart_pattern_pop_pos(EX_UART_NUM);
#if HLW8032_DEBUG
                ESP_LOGI(TAG, "[UART PATTERN DETECTED] pos: %d, buffered size: %d", pos, buffered_size);
#endif
                if (pos == -1)
                {
                    // There used to be a UART_PATTERN_DET event, but the pattern position queue is full so that it can not
                    // record the position. We should set a larger queue size.
                    // As an example, we directly flush the rx buffer here.
                    uart_flush_input(EX_UART_NUM);
                }
                else
                {
                    uart_read_bytes(EX_UART_NUM, dtmp, pos, 100 / portTICK_PERIOD_MS);
                    uint8_t pat[PATTERN_CHR_NUM + 1];
                    memset(pat, 0, sizeof(pat));
                    uart_read_bytes(EX_UART_NUM, pat, PATTERN_CHR_NUM, 100 / portTICK_PERIOD_MS);
#if HLW8032_DEBUG
                    ESP_LOGI(TAG, "read data: %s", dtmp);
                    ESP_LOGI(TAG, "read pat : %s", pat);
#endif
                }
                break;
            default: /* Others */
#if HLW8032_DEBUG
                ESP_LOGI(TAG, "uart event type: %d", event.type);
#endif
                break;
            }
        }
    }
    free(dtmp);
    dtmp = NULL;
    vTaskDelete(NULL);
}
